using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using Gateway.Common.Cache;
using Gateway.Infrastructure.Provider.Operator;
using Gateway.IQuery.Sign;
using Gateway.UI.Api.Filters.Attributes;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc.Filters;

namespace Gateway.UI.Api.Filters;

/// <summary>
/// 权限认证
/// </summary>
public class AuthorizationFilter : Attribute, IAsyncAuthorizationFilter
{
    private readonly ITokenService _tokenService;
    private readonly IRedisCache _redisCache;
    private readonly IOperatorProvider _operatorProvider;
    private readonly IAuthService _authService;

    /// <summary>
    /// 
    /// </summary>
    /// <param name="tokenService"></param>
    /// <param name="redisCache"></param>
    /// <param name="operatorProvider"></param>
    /// <param name="authService"></param>
    public AuthorizationFilter(ITokenService tokenService,
        IRedisCache redisCache, IOperatorProvider operatorProvider, IAuthService authService)
    {
        _tokenService = tokenService;
        _redisCache = redisCache;
        _operatorProvider = operatorProvider;
        _authService = authService;
    }

    /// <summary>
    /// 
    /// </summary>
    /// <param name="context"></param>
    public async Task OnAuthorizationAsync(AuthorizationFilterContext context)
    {
        //if (SkipAuthorizationByPath(context))
        //    return;
        //判断是否需要跳过授权验证
        if (context.ActionDescriptor.EndpointMetadata.Any(x => x is AllowAnonymousAttribute))
            return;

        //判断授权是否通过
        var isAuthenticated = context.HttpContext.User.Identity?.IsAuthenticated ?? false;
        if (!isAuthenticated)
        {
            ApiResult ajaxResult = new ApiResult(SystemErrorCode.LoginExpress);
            context.Result = new JsonResult(ajaxResult);
            return;
        }

        //检查接口权限
        if (context.ActionDescriptor.EndpointMetadata.Any(x => x is CheckApiAttribute))
        {
            var url = context.HttpContext.Request.Path;
            string checkUrl = url.Value?.TrimStart('/') ?? "";
            Console.WriteLine("当前请求地址:" + checkUrl);
            var apis = _redisCache.Get<List<string>>(CacheKeys.UserCacheApi +
                                                     _operatorProvider.GetOperator().OperatorId);
            if (apis == null)
            {
                //重新拉取 然后渲染缓存
                apis = await _authService.ResetApisWithOwner();
            }

            if (apis == null || !apis.Exists(p => p.Equals(checkUrl)))
            {
                context.Result = new JsonResult(new ApiResult(SystemErrorCode.NoAuth));
                return;
            }
        }

        //判断是否需要刷新Token
        if (context.HttpContext.Request.Headers.TryGetValue("Authorization", out var authHeader))
        {
            var token = authHeader.ToString().Split(' ')[1];
            var jwtToken = new JwtSecurityTokenHandler().ReadJwtToken(token);
            var jwtPayload = jwtToken.Payload; //获取payload中的数据 

            if (jwtPayload.Expiration != null)
            {
                var expTime = jwtPayload.Expiration.Value;
                var now = DateTime.Now.ToTimestamp();
                //如果Token已过期，重新生成Token放在返回头中返回
                //Token过期时间为设置的过期时间加缓冲过期时间,相当于在缓冲过期时间段内进行请求,将返回一个新的Token
                if (now > expTime)
                {
                    var claims = jwtPayload.Claims as List<Claim>;
                    var userId = claims?.FirstOrDefault(x => x.Type == ClaimTypes.NameIdentifier)?.Value ??
                                 string.Empty;
                    var userName = claims?.FirstOrDefault(x => x.Type == ClaimTypes.Name)?.Value ?? string.Empty;
                    var refreshToken = await _tokenService.CreateTokenAsync(userId, userName);
                    context.HttpContext.Response.Headers.Append("Set-Authorization", refreshToken);
                }
            }
        }
    }

    private bool SkipAuthorizationByPath(AuthorizationFilterContext context)
    {
        return true;
    }
}